home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / DistUpgrade / distro.py < prev    next >
Text File  |  2009-09-25  |  18KB  |  463 lines

  1. #  distro.py - Provide a distro abstraction of the sources.list
  2. #
  3. #  Copyright (c) 2004-2009 Canonical Ltd.
  4. #  Copyright (c) 2006-2007 Sebastian Heinlein
  5. #
  6. #  Authors: Sebastian Heinlein <glatzor@ubuntu.com>
  7. #           Michael Vogt <mvo@debian.org>
  8. #
  9. #  This program is free software; you can redistribute it and/or
  10. #  modify it under the terms of the GNU General Public License as
  11. #  published by the Free Software Foundation; either version 2 of the
  12. #  License, or (at your option) any later version.
  13. #
  14. #  This program is distributed in the hope that it will be useful,
  15. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17. #  GNU General Public License for more details.
  18. #
  19. #  You should have received a copy of the GNU General Public License
  20. #  along with this program; if not, write to the Free Software
  21. #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  22. #  USA
  23.  
  24. import gettext
  25. import re
  26. import os
  27. import sys
  28.  
  29. from xml.etree.ElementTree import ElementTree
  30. import gettext
  31.  
  32.  
  33. def _(s):
  34.     return gettext.dgettext("python-apt", s)
  35.  
  36.  
  37. class NoDistroTemplateException(Exception):
  38.     pass
  39.  
  40.  
  41. class Distribution:
  42.  
  43.     def __init__(self, id, codename, description, release):
  44.         """ Container for distribution specific informations """
  45.         # LSB information
  46.         self.id = id
  47.         self.codename = codename
  48.         self.description = description
  49.         self.release = release
  50.  
  51.         self.binary_type = "deb"
  52.         self.source_type = "deb-src"
  53.  
  54.     def get_sources(self, sourceslist):
  55.         """
  56.         Find the corresponding template, main and child sources
  57.         for the distribution
  58.         """
  59.  
  60.         self.sourceslist = sourceslist
  61.         # corresponding sources
  62.         self.source_template = None
  63.         self.child_sources = []
  64.         self.main_sources = []
  65.         self.disabled_sources = []
  66.         self.cdrom_sources = []
  67.         self.download_comps = []
  68.         self.enabled_comps = []
  69.         self.cdrom_comps = []
  70.         self.used_media = []
  71.         self.get_source_code = False
  72.         self.source_code_sources = []
  73.  
  74.         # location of the sources
  75.         self.default_server = ""
  76.         self.main_server = ""
  77.         self.nearest_server = ""
  78.         self.used_servers = []
  79.  
  80.         # find the distro template
  81.         for template in self.sourceslist.matcher.templates:
  82.             if (self.is_codename(template.name) and
  83.                 template.distribution == self.id):
  84.                 #print "yeah! found a template for %s" % self.description
  85.                 #print template.description, template.base_uri, \
  86.                 #    template.components
  87.                 self.source_template = template
  88.                 break
  89.         if self.source_template is None:
  90.             raise NoDistroTemplateException("Error: could not find a "
  91.                                                 "distribution template")
  92.  
  93.         # find main and child sources
  94.         media = []
  95.         comps = []
  96.         cdrom_comps = []
  97.         enabled_comps = []
  98.         source_code = []
  99.         for source in self.sourceslist.list:
  100.             if (source.invalid == False and
  101.                 self.is_codename(source.dist) and
  102.                 source.template and
  103.                 self.is_codename(source.template.name)):
  104.                 #print "yeah! found a distro repo:  %s" % source.line
  105.                 # cdroms need do be handled differently
  106.                 if (source.uri.startswith("cdrom:") and
  107.                     source.disabled == False):
  108.                     self.cdrom_sources.append(source)
  109.                     cdrom_comps.extend(source.comps)
  110.                 elif (source.uri.startswith("cdrom:") and
  111.                       source.disabled == True):
  112.                     self.cdrom_sources.append(source)
  113.                 elif (source.type == self.binary_type and
  114.                       source.disabled == False):
  115.                     self.main_sources.append(source)
  116.                     comps.extend(source.comps)
  117.                     media.append(source.uri)
  118.                 elif (source.type == self.binary_type and
  119.                       source.disabled == True):
  120.                     self.disabled_sources.append(source)
  121.                 elif (source.type == self.source_type
  122.                         and source.disabled == False):
  123.                     self.source_code_sources.append(source)
  124.                 elif (source.type == self.source_type and
  125.                       source.disabled == True):
  126.                     self.disabled_sources.append(source)
  127.             if (source.invalid == False and
  128.                 source.template in self.source_template.children):
  129.                 if (source.disabled == False
  130.                     and source.type == self.binary_type):
  131.                     self.child_sources.append(source)
  132.                 elif (source.disabled == False
  133.                       and source.type == self.source_type):
  134.                     self.source_code_sources.append(source)
  135.                 else:
  136.                     self.disabled_sources.append(source)
  137.         self.download_comps = set(comps)
  138.         self.cdrom_comps = set(cdrom_comps)
  139.         enabled_comps.extend(comps)
  140.         enabled_comps.extend(cdrom_comps)
  141.         self.enabled_comps = set(enabled_comps)
  142.         self.used_media = set(media)
  143.         self.get_mirrors()
  144.  
  145.     def get_mirrors(self, mirror_template=None):
  146.         """
  147.         Provide a set of mirrors where you can get the distribution from
  148.         """
  149.         # the main server is stored in the template
  150.         self.main_server = self.source_template.base_uri
  151.  
  152.         # other used servers
  153.         for medium in self.used_media:
  154.             if not medium.startswith("cdrom:"):
  155.                 # seems to be a network source
  156.                 self.used_servers.append(medium)
  157.  
  158.         if len(self.main_sources) == 0:
  159.             self.default_server = self.main_server
  160.         else:
  161.             self.default_server = self.main_sources[0].uri
  162.  
  163.         # get a list of country codes and real names
  164.         self.countries = {}
  165.         fname = "/usr/share/xml/iso-codes/iso_3166.xml"
  166.         if os.path.exists(fname):
  167.             et = ElementTree(file=fname)
  168.             it = et.getiterator('iso_3166_entry')
  169.             for elm in it:
  170.                 try:
  171.                     descr = elm.attrib["common_name"]
  172.                 except KeyError:
  173.                     descr = elm.attrib["name"]
  174.                 try:
  175.                     code = elm.attrib["alpha_2_code"]
  176.                 except KeyError:
  177.                     code = elm.attrib["alpha_3_code"]
  178.                 self.countries[code.lower()] = gettext.dgettext('iso_3166',
  179.                                                                 descr)
  180.  
  181.         # try to guess the nearest mirror from the locale
  182.         self.country = None
  183.         self.country_code = None
  184.         locale = os.getenv("LANG", default="en_UK")
  185.         a = locale.find("_")
  186.         z = locale.find(".")
  187.         if z == -1:
  188.             z = len(locale)
  189.         country_code = locale[a+1:z].lower()
  190.  
  191.         if mirror_template:
  192.             self.nearest_server = mirror_template % country_code
  193.  
  194.         if country_code in self.countries:
  195.             self.country = self.countries[country_code]
  196.             self.country_code = country_code
  197.  
  198.     def _get_mirror_name(self, server):
  199.         ''' Try to get a human readable name for the main mirror of a country
  200.             Customize for different distributions '''
  201.         country = None
  202.         i = server.find("://")
  203.         l = server.find(".archive.ubuntu.com")
  204.         if i != -1 and l != -1:
  205.             country = server[i+len("://"):l]
  206.         if country in self.countries:
  207.             # TRANSLATORS: %s is a country
  208.             return _("Server for %s") % self.countries[country]
  209.         else:
  210.             return("%s" % server.rstrip("/ "))
  211.  
  212.     def get_server_list(self):
  213.         ''' Return a list of used and suggested servers '''
  214.  
  215.         def compare_mirrors(mir1, mir2):
  216.             ''' Helper function that handles comaprision of mirror urls
  217.                 that could contain trailing slashes'''
  218.             return re.match(mir1.strip("/ "), mir2.rstrip("/ "))
  219.  
  220.         # Store all available servers:
  221.         # Name, URI, active
  222.         mirrors = []
  223.         if (len(self.used_servers) < 1 or
  224.             (len(self.used_servers) == 1 and
  225.              compare_mirrors(self.used_servers[0], self.main_server))):
  226.             mirrors.append([_("Main server"), self.main_server, True])
  227.             if self.nearest_server:
  228.                 mirrors.append([self._get_mirror_name(self.nearest_server),
  229.                                 self.nearest_server, False])
  230.         elif (len(self.used_servers) == 1 and not
  231.               compare_mirrors(self.used_servers[0], self.main_server)):
  232.             mirrors.append([_("Main server"), self.main_server, False])
  233.             # Only one server is used
  234.             server = self.used_servers[0]
  235.  
  236.             # Append the nearest server if it's not already used
  237.             if self.nearest_server:
  238.                 if not compare_mirrors(server, self.nearest_server):
  239.                     mirrors.append([self._get_mirror_name(self.nearest_server),
  240.                                     self.nearest_server, False])
  241.             if server:
  242.                 mirrors.append([self._get_mirror_name(server), server, True])
  243.  
  244.         elif len(self.used_servers) > 1:
  245.             # More than one server is used. Since we don't handle this case
  246.             # in the user interface we set "custom servers" to true and
  247.             # append a list of all used servers
  248.             mirrors.append([_("Main server"), self.main_server, False])
  249.             if self.nearest_server:
  250.                 mirrors.append([self._get_mirror_name(self.nearest_server),
  251.                                 self.nearest_server, False])
  252.             mirrors.append([_("Custom servers"), None, True])
  253.             for server in self.used_servers:
  254.                 mirror_entry = [self._get_mirror_name(server), server, False]
  255.                 if (compare_mirrors(server, self.nearest_server) or
  256.                     compare_mirrors(server, self.main_server)):
  257.                     continue
  258.                 elif not mirror_entry in mirrors:
  259.                     mirrors.append(mirror_entry)
  260.  
  261.         return mirrors
  262.  
  263.     def add_source(self, type=None,
  264.                  uri=None, dist=None, comps=None, comment=""):
  265.         """
  266.         Add distribution specific sources
  267.         """
  268.         if uri is None:
  269.             # FIXME: Add support for the server selector
  270.             uri = self.default_server
  271.         if dist is None:
  272.             dist = self.codename
  273.         if comps is None:
  274.             comps = list(self.enabled_comps)
  275.         if type is None:
  276.             type = self.binary_type
  277.         new_source = self.sourceslist.add(type, uri, dist, comps, comment)
  278.         # if source code is enabled add a deb-src line after the new
  279.         # source
  280.         if self.get_source_code == True and type == self.binary_type:
  281.             self.sourceslist.add(self.source_type, uri, dist, comps, comment,
  282.                                  file=new_source.file,
  283.                                  pos=self.sourceslist.list.index(new_source)+1)
  284.  
  285.     def enable_component(self, comp):
  286.         """
  287.         Enable a component in all main, child and source code sources
  288.         (excluding cdrom based sources)
  289.  
  290.         comp:         the component that should be enabled
  291.         """
  292.  
  293.         def add_component_only_once(source, comps_per_dist):
  294.             """
  295.             Check if we already added the component to the repository, since
  296.             a repository could be splitted into different apt lines. If not
  297.             add the component
  298.             """
  299.             # if we don't that distro, just reutnr (can happen for e.g.
  300.             # dapper-update only in deb-src
  301.             if source.dist not in comps_per_dist:
  302.                 return
  303.             # if we have seen this component already for this distro,
  304.             # return (nothing to do
  305.             if comp in comps_per_dist[source.dist]:
  306.                 return
  307.             # add it
  308.             source.comps.append(comp)
  309.             comps_per_dist[source.dist].add(comp)
  310.  
  311.         sources = []
  312.         sources.extend(self.main_sources)
  313.         sources.extend(self.child_sources)
  314.         # store what comps are enabled already per distro (where distro is
  315.         # e.g. "dapper", "dapper-updates")
  316.         comps_per_dist = {}
  317.         comps_per_sdist = {}
  318.         for s in sources:
  319.             if s.type == self.binary_type:
  320.                 if s.dist not in comps_per_dist:
  321.                     comps_per_dist[s.dist] = set()
  322.                 map(comps_per_dist[s.dist].add, s.comps)
  323.         for s in self.source_code_sources:
  324.             if s.type == self.source_type:
  325.                 if s.dist not in comps_per_sdist:
  326.                     comps_per_sdist[s.dist] = set()
  327.                 map(comps_per_sdist[s.dist].add, s.comps)
  328.  
  329.         # check if there is a main source at all
  330.         if len(self.main_sources) < 1:
  331.             # create a new main source
  332.             self.add_source(comps=["%s"%comp])
  333.         else:
  334.             # add the comp to all main, child and source code sources
  335.             for source in sources:
  336.                 add_component_only_once(source, comps_per_dist)
  337.  
  338.         # check if there is a main source code source at all
  339.         if self.get_source_code == True:
  340.             if len(self.source_code_sources) < 1:
  341.                 # create a new main source
  342.                 self.add_source(type=self.source_type, comps=["%s"%comp])
  343.             else:
  344.                 # add the comp to all main, child and source code sources
  345.                 for source in self.source_code_sources:
  346.                     add_component_only_once(source, comps_per_sdist)
  347.  
  348.     def disable_component(self, comp):
  349.         """
  350.         Disable a component in all main, child and source code sources
  351.         (excluding cdrom based sources)
  352.         """
  353.         sources = []
  354.         sources.extend(self.main_sources)
  355.         sources.extend(self.child_sources)
  356.         sources.extend(self.source_code_sources)
  357.         if comp in self.cdrom_comps:
  358.             sources = []
  359.             sources.extend(self.main_sources)
  360.         for source in sources:
  361.             if comp in source.comps:
  362.                 source.comps.remove(comp)
  363.                 if len(source.comps) < 1:
  364.                     self.sourceslist.remove(source)
  365.  
  366.     def change_server(self, uri):
  367.         ''' Change the server of all distro specific sources to
  368.             a given host '''
  369.  
  370.         def change_server_of_source(source, uri, seen):
  371.             # Avoid creating duplicate entries
  372.             source.uri = uri
  373.             for comp in source.comps:
  374.                 if [source.uri, source.dist, comp] in seen:
  375.                     source.comps.remove(comp)
  376.                 else:
  377.                     seen.append([source.uri, source.dist, comp])
  378.             if len(source.comps) < 1:
  379.                 self.sourceslist.remove(source)
  380.  
  381.         seen_binary = []
  382.         seen_source = []
  383.         self.default_server = uri
  384.         for source in self.main_sources:
  385.             change_server_of_source(source, uri, seen_binary)
  386.         for source in self.child_sources:
  387.             # Do not change the forces server of a child source
  388.             if (source.template.base_uri is None or
  389.                 source.template.base_uri != source.uri):
  390.                 change_server_of_source(source, uri, seen_binary)
  391.         for source in self.source_code_sources:
  392.             change_server_of_source(source, uri, seen_source)
  393.  
  394.     def is_codename(self, name):
  395.         ''' Compare a given name with the release codename. '''
  396.         if name == self.codename:
  397.             return True
  398.         else:
  399.             return False
  400.  
  401.  
  402. class DebianDistribution(Distribution):
  403.     ''' Class to support specific Debian features '''
  404.  
  405.     def is_codename(self, name):
  406.         ''' Compare a given name with the release codename and check if
  407.             if it can be used as a synonym for a development releases '''
  408.         if name == self.codename or self.release in ("testing", "unstable"):
  409.             return True
  410.         else:
  411.             return False
  412.  
  413.     def _get_mirror_name(self, server):
  414.         ''' Try to get a human readable name for the main mirror of a country
  415.             Debian specific '''
  416.         country = None
  417.         i = server.find("://ftp.")
  418.         l = server.find(".debian.org")
  419.         if i != -1 and l != -1:
  420.             country = server[i+len("://ftp."):l]
  421.         if country in self.countries:
  422.             # TRANSLATORS: %s is a country
  423.             return _("Server for %s") % gettext.dgettext(
  424.                 "iso_3166", self.countries[country].rstrip()).rstrip()
  425.         else:
  426.             return("%s" % server.rstrip("/ "))
  427.  
  428.     def get_mirrors(self):
  429.         Distribution.get_mirrors(
  430.             self, mirror_template="http://ftp.%s.debian.org/debian/")
  431.  
  432.  
  433. class UbuntuDistribution(Distribution):
  434.     ''' Class to support specific Ubuntu features '''
  435.  
  436.     def get_mirrors(self):
  437.         Distribution.get_mirrors(
  438.             self, mirror_template="http://%s.archive.ubuntu.com/ubuntu/")
  439.  
  440.  
  441. def get_distro(id=None, codename=None, description=None, release=None):
  442.     """
  443.     Check the currently used distribution and return the corresponding
  444.     distriubtion class that supports distro specific features.
  445.  
  446.     If no paramter are given the distro will be auto detected via
  447.     a call to lsb-release
  448.     """
  449.     # make testing easier
  450.     if not (id and codename and description and release):
  451.         lsb_info = []
  452.         for lsb_option in ["-i", "-c", "-d", "-r"]:
  453.             pipe = os.popen("lsb_release %s -s" % lsb_option)
  454.             lsb_info.append(pipe.read().strip())
  455.             del pipe
  456.         (id, codename, description, release) = lsb_info
  457.     if id == "Ubuntu":
  458.         return UbuntuDistribution(id, codename, description, release)
  459.     elif id == "Debian":
  460.         return DebianDistribution(id, codename, description, release)
  461.     else:
  462.         return Distribution(id, codename, description, release)
  463.